home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Skunkware 5
/
Skunkware 5.iso
/
src
/
X11
/
xmbase-grok-1.2
/
formop.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-06-25
|
18KB
|
615 lines
/*
* this file is part of the form editor, it is mostly called by callbacks in
* formwin.c. It allocates forms and items, finds good defaults for things,
* and does some form geometry operations. Actual form drawing is done in
* preview.c (for the edit canvas) and cardwin.c (for final cards).
*
* form_create() allocate form with default parms
* form_delete(form) free form and all items in it
* verify_form(form,bug,shell) make sure that form is consistent
* form_edit_script(form,sh,fn) start up editor for form script
* form_sort(form) sort form's items by y/x position
*
* item_deselect(form) deselect all items in canvas
* item_create(form, nitem) allocate item and figure out defaults
* item_delete(form, nitem) free item and remove from form
*/
#include "config.h"
#include <X11/Xos.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <Xm/Xm.h>
#include "grok.h"
#include "form.h"
#include "proto.h"
#define CHUNK 10 /* alloc 10 new item ptrs at a time */
#define XSNAP(x) ((x)-(x)%form->xg)
#define YSNAP(y) ((y)-(y)%form->yg)
static void set_form_defaults(FORM *);
extern Widget toplevel; /* top-level shell for icon name */
/*------------------------------------------------- operations on form ------*/
/*
* allocate a new empty form struct, and fill it with reasonable default
* paramaters.
*/
FORM *form_create(void)
{
FORM *form; /* new form */
if (!(form = (FORM *)malloc(sizeof(FORM)))) {
create_error_popup(toplevel, errno, "Can't create form");
return(0);
}
set_form_defaults(form);
return(form);
}
static void set_form_defaults(
FORM *form) /* for to initialize */
{
mybzero((void *)form, sizeof(FORM));
form->cdelim = ':';
form->xg = 4;
form->yg = 4;
form->xs = XSNAP(400);
form->ys = YSNAP(200);
form->autoquery = -1;
}
/*
* clone a form. This is done when the form editor is started with a template
* form, we mustn't overwrite the form currently being displayed in the main
* menu.
*/
FORM *form_clone(
FORM *parent) /* old form */
{
FORM *form; /* new form */
int i;
if (!(form = (FORM *)malloc(sizeof(FORM)))) {
create_error_popup(toplevel, errno, "Can't clone form");
return(0);
}
*form = *parent;
form->path = mystrdup(parent->path);
form->name = mystrdup(parent->name);
form->dbase = mystrdup(parent->dbase);
form->comment = mystrdup(parent->comment);
form->help = mystrdup(parent->help);
if (form->nitems &&
!(form->items = (ITEM **)malloc(form->size * sizeof(ITEM *)))) {
create_error_popup(toplevel, errno, "Can't clone field list");
form->nitems = 0;
}
for (i=0; i < form->nitems; i++)
form->items[i] = item_clone(parent->items[i]);
if (form->query) {
if (!(form->query = malloc(form->nqueries * sizeof(DQUERY)))) {
create_error_popup(toplevel, errno, "Queries lost");
form->nqueries = 0;
}
for (i=0; i < form->nqueries; i++) {
form->query[i].suspended = parent->query[i].suspended;
form->query[i].name = mystrdup(parent->query[i].name);
form->query[i].query= mystrdup(parent->query[i].query);
}
}
return(form);
}
/*
* destroy a form struct and all its items. The pointer passed is not freed.
* Make sure to remove all windows that display a view of this form first.
* Initialize the form with defaults (this is used when reading form file).
*/
void form_delete(
FORM *form) /* form to delete */
{
DQUERY *dq; /* default query entry */
int i;
if (!form)
return;
for (i=0; i < form->nitems; i++)
item_delete(form, i);
if (form->path) free((void *)form->path);
if (form->name) free((void *)form->name);
if (form->dbase) free((void *)form->dbase);
if (form->comment) free((void *)form->comment);
if (form->help) free((void *)form->help);
if (form->query) {
for (i=0; i < form->nqueries; i++) {
dq = &form->query[i];
if (dq->name) free((void *)dq->name);
if (dq->query) free((void *)dq->query);
}
free((void *)form->query);
}
set_form_defaults(form);
}
/*
* print an error report and return FALSE if there are problems with the
* form. If there is an error, set *bug to the item# of the first buggy
* item; otherwise set to form->nitems. This is used to highlight the
* first incorrect item.
*/
#define ISFLAG(i) (i->type == IT_FLAG || i->type == IT_CHOICE)
BOOL verify_form(
FORM *form, /* form to verify */
int *bug, /* retuirned buggy item # */
Widget shell) /* error popup parent */
{
int nitem, ni; /* item counter */
ITEM *item, *it; /* item pointer */
int nq; /* query pointer */
DQUERY *dq; /* query pointer */
char name[256]; /* current item's name */
char msg[16384]; /* error messages */
int i0, i = 0; /* next free byte in msg[] */
int sumwidth = 0; /* total length of summary */
if (bug)
*bug = form->nitems;
if (!form->name || !*form->name) {
sprintf(msg+i, "Form has no name\n");
i += strlen(msg+i);
}
if (!form->dbase || !*form->dbase) {
sprintf(msg+i, "Form has no database%s\n",
form->name ? "using form name" : "");
i += strlen(msg+i);
form->dbase = mystrdup(form->name);
}
if (form->cdelim < 1) {
sprintf(msg+i, "Illegal field delimiter, using TAB\n");
i += strlen(msg+i);
form->cdelim = '\t';
}
for (nitem=0; nitem < form->nitems; nitem++) {
i0 = i;
item = form->items[nitem];
sumwidth += item->sumwidth;
sprintf(name, "Field \"%s\" (#%d)",
item->name ? item->name : "", nitem);
if (!item->name || !*item->name) {
char newname[40];
sprintf(newname, "item%d", nitem);
sprintf(msg+i, "%s has no internal name, using %s\n",
name, newname);
i += strlen(msg+i);
item->name = mystrdup(newname);
}
if (item->xs <= 0 || item->ys <= 0) {
if (!item->xs) item->xs = 10;
if (!item->ys) item->ys = 10;
sprintf(msg+i, "%s has zero size, setting to %d %d\n",
name, item->xs, item->ys);
i += strlen(msg+i);
}
if (!item->label && item->type != IT_VIEW) {
sprintf(msg+i, "%s has no label, using \"%s\"\n",
name, name);
i += strlen(msg+i);
item->label = mystrdup(name);
}
if (!item->flagcode && ISFLAG(item)) {
sprintf(msg+i, "%s has no flag code\n", name);
i += strlen(msg+i);
}
if (!item->pressed && item->type == IT_BUTTON) {
sprintf(msg+i, "%s has no button action\n", name);
i += strlen(msg+i);
}
if (!item->database && item->type == IT_VIEW) {
sprintf(msg+i, "%s has no query database\n", name);
i += strlen(msg+i);
}
if (!item->query && item->type == IT_VIEW) {
sprintf(msg+i, "%s has no query\n", name);
i += strlen(msg+i);
}
for (nq=0; nq < form->nqueries; nq++) {
dq = &form->query[nq];
if (!dq->suspended) {
if (!dq->name) {
sprintf(msg+i, "Query %d has no name\n", nq+1);
i += strlen(msg+i);
}
if (!dq->query) {
sprintf(msg+i, "Query %d has no query\n",nq+1);
i += strlen(msg+i);
}
}
}
for (ni=nitem+1; ni < form->nitems; ni++) {
it = form->items[ni];
if (item->type == IT_CHOICE && it->type == IT_CHOICE) {
BOOL eq = !strcmp(item->name, it->name);
BOOL sam = FALSE;
char *m = 0;
if ((item->column == it->column) != eq)
m = "dbase column";
else if ((item->sumcol == it->sumcol) != eq)
m = "summary column";
else if ((item->sumwidth == it->sumwidth)!=eq)
m = "summary width";
else if ((item->flagcode == it->flagcode)&&eq){
m = "flag code";
sam = TRUE;
}
if (m && (eq || sam)) {
sprintf(msg+i,
"(choice) has %s internal name as #%d, but has %s %s\n",
eq ? "same" : "different", ni,
sam ? "same" : "different", m);
i += strlen(msg+i);
}
continue;
}
if (item->name && it->name
&& !strcmp(item->name, it->name)) {
sprintf(msg+i,"%s has same name as field #%d\n"
,name, ni);
i += strlen(msg+i);
}
if (IN_DBASE(item->type) && IN_DBASE(it->type)
&& item->column == it->column
&& (!ISFLAG(item) || !ISFLAG(it))) {
sprintf(msg+i,
"%s uses same dbase column as field \"%s\" (#%d)\n",
name, it->name ? it->name : "", ni);
i += strlen(msg+i);
}
if (IN_DBASE(item->type) && IN_DBASE(it->type)
&& item->sumwidth>0 && it->sumwidth>0
&& item->sumcol == it->sumcol) {
sprintf(msg+i,
"%s uses same summary column as field \"%s\" (#%d)\n",
name, it->name ? it->name : "", ni);
i += strlen(msg+i);
}
if (i > sizeof(msg)-1024) {
sprintf(msg+i, "Too many errors, aborted.");
break;
}
}
if (i > sizeof(msg)-1024) {
sprintf(msg+i, "Too many errors, aborted.");
break;
}
if (i > i0 && bug && *bug == form->nitems)
*bug = nitem;
}
if (form->nitems && sumwidth == 0) {
sprintf(msg+i, "Summary is empty, all summary widths are 0\n");
i += strlen(msg+i);
}
if (i)
create_error_popup(shell, 0, msg);
else {
for (i=nitem=0; nitem < form->nitems; nitem++) {
item = form->items[nitem];
if (item->type == IT_NOTE && item->maxlen <= 100) {
sprintf(msg+i,
"Warning: note field \"%s\" (#%d) has short max length %d",
item->name ? item->name : "", nitem,
item->maxlen);
i += strlen(msg+i);
}
}
if (i)
create_error_popup(shell, 0, msg);
i = 0;
}
return(!i);
}
/*
* the database is procedural, and the edit button was pressed. Start up an
* editor window with fname, up to the first blank (this is useful for passing
* cmd line options later).
*/
void form_edit_script(
FORM *form, /* form to edit */
Widget shell, /* error popup parent */
char *fname) /* file name of script (dbase name) */
{
char path[1024], *p, *q;
if (!fname || !*fname) {
create_error_popup(shell, 0,
"Please specify a database name first.\n\
The database name will be used as script name.");
return;
}
form->proc = TRUE;
fillout_formedit_widget_by_code(0x105);
for (p=fname, q=path; *p && *p != ' ' && *p != '\t'; p++, q++)
*q = *p;
*q = 0;
fname = resolve_tilde(path, 0);
edit_file(fname, FALSE, TRUE, "Procedural Database", "procdbedit");
}
/*
* sort the form's items by y, then x position (lower left corner). Later
* traversal in order can then be done simply by going down the list.
*/
static int icompare(
MYCONST void *u,
MYCONST void *v)
{
register ITEM *iu = *(ITEM **)u;
register ITEM *iv = *(ITEM **)v;
return(iu->y+iu->ys == iv->y+iv->ys ? iu->x+iu->xs - iv->x-iv->xs
: iu->y+iu->ys - iv->y-iv->ys);
}
void form_sort(
register FORM *form) /* form to sort */
{
if (!form || !form->items || !form->nitems)
return;
qsort((void *)form->items, form->nitems, sizeof(ITEM *), icompare);
}
/*------------------------------------------------- operations on form items */
/*
* deselect all items. The caller must make sure to set curr_item to the
* number of items in the current form (which means no selection). Here, only
* the selected flags are cleared. This happens when Unselect is pressed, but
* also if an item is added (we only want the added one selected afterwards).
*/
void item_deselect(
register FORM *form) /* describes form and all items in it*/
{
register ITEM *item; /* new item struct */
int i; /* index of item */
for (i=0; i < form->nitems; i++) {
item = form->items[i];
if (item->selected) {
item->selected = FALSE;
redraw_canvas_item(item);
}
}
}
/*
* insert an item into the form, at the current field <nitem>. Fields are
* stored in the form, which contains an array of pointers that point to
* the field data structures. That way, we don't need to re-allocate the
* form struct itself, and items can be easily re-arranged.
* This assumes that fe_item_deselect has been called.
* Provide reasonable defaults for the new field.
*/
BOOL item_create(
register FORM *form, /* describes form and all items in it*/
int nitem) /* the current item, insert point */
{
register ITEM *item; /* new item struct */
register ITEM *prev; /* prev item, plundered for defaults */
register int i, j, n, t; /* various counters */
char buf[80]; /* temp for default strings */
/* allocate array */
if (!form->items || form->nitems >= form->size) {
i = (form->size + CHUNK) * sizeof(ITEM *);
if (!(form->items = (ITEM **)(form->items
? realloc((void *)form->items, i)
: malloc(i)))) {
create_error_popup(toplevel, errno,
"Can't create field");
return(FALSE);
}
form->size += CHUNK;
}
/* allocate item */
if (!(item = (ITEM *)malloc(sizeof(ITEM)))) {
create_error_popup(toplevel, errno, "Can't create field");
return(FALSE);
}
for (i=form->nitems-1; i >= nitem; i--)
form->items[i+1] = form->items[i];
form->items[nitem] = item;
form->nitems++;
/* defaults */
if (nitem < form->nitems-1) {
(void)memcpy(item, form->items[nitem+1], sizeof(ITEM));
item->name = mystrdup(item->name);
item->flagcode = mystrdup(item->flagcode);
item->label = mystrdup(item->label);
item->gray_if = mystrdup(item->gray_if);
item->freeze_if = mystrdup(item->freeze_if);
item->invisible_if = mystrdup(item->invisible_if);
item->skip_if = mystrdup(item->skip_if);
item->idefault = mystrdup(item->idefault);
item->pattern = mystrdup(item->pattern);
item->pressed = mystrdup(item->pressed);
item->added = mystrdup(item->added);
item->database = mystrdup(item->database);
item->query = mystrdup(item->query);
} else {
mybzero((void *)item, sizeof(ITEM));
item->type = IT_INPUT;
item->selected = TRUE;
item->labeljust = J_LEFT;
item->inputjust = J_LEFT;
item->column = 1;
item->minlen = 1;
item->maxlen = 100;
item->ch_xmax = 1;
item->ch_ymax = 1;
item->qsummary = TRUE;
item->qlast = TRUE;
item->x = XSNAP(8) + form->xg;
item->xs = XSNAP(form->xs - 32);
item->xm = XSNAP(item->xs / 4);
item->y = YSNAP(8) + form->xg;
item->ys = YSNAP(24) + form->yg;
item->ym = item->ys;
if (nitem) {
prev = form->items[nitem-1];
for (i=0; i < form->nitems; i++)
if (form->items[i]->type == item->type) {
prev = form->items[i];
item->xs = prev->xs;
item->ys = prev->xs;
item->xm = prev->ym;
item->ym = prev->ym;
break;
}
item->labelfont = prev->labelfont;
item->inputfont = prev->inputfont;
}
}
/* move to bottom */
for (i=0; i < form->nitems; i++) {
j = form->items[i]->y + form->items[i]->ys;
j = YSNAP(j + 8);
if (i != nitem && j > item->y)
item->y = j;
}
/* find unused column*/
item->column = 0;
if (item->type == IT_CHOICE || item->type == IT_FLAG)
item->flagcode = 0;
else {
if (IN_DBASE(item->type)) {
n = form->nitems;
for (i=0; i < n; i++)
for (j=0; j < n; j++) {
t = form->items[j]->type;
if (IN_DBASE(t) &&
form->items[j] != item &&
form->items[j]->column ==
item->column) {
item->column++;
break;
}
}
}
sprintf(buf, "item%d", item->column);
item->name = mystrdup(buf);
}
return(TRUE);
}
/*
* delete a field from the form definition.
*/
void item_delete(
FORM *form, /* describes form and all items in it*/
int nitem) /* the current item, insert point */
{
register ITEM *item = form->items[nitem];
int i;
if (item->name) free((void *)item->name);
if (item->label) free((void *)item->label);
if (item->flagcode) free((void *)item->flagcode);
if (item->gray_if) free((void *)item->gray_if);
if (item->freeze_if) free((void *)item->freeze_if);
if (item->invisible_if) free((void *)item->invisible_if);
if (item->skip_if) free((void *)item->skip_if);
if (item->idefault) free((void *)item->idefault);
if (item->pattern) free((void *)item->pattern);
if (item->pressed) free((void *)item->pressed);
if (item->added) free((void *)item->added);
if (item->ch_xexpr) free((void *)item->ch_xexpr);
if (item->ch_yexpr) free((void *)item->ch_yexpr);
if (item->ch_bar) free((void *)item->ch_bar);
if (item->database) free((void *)item->database);
if (item->query) free((void *)item->query);
for (i=0; i < item->ch_ncomp; i++) {
item->ch_curr = i;
del_chart_component(item);
}
if (item->ch_comp)
free((void *)item->ch_comp);
free((void *)item);
for (i=nitem; i < form->nitems-1; i++)
form->items[i] = form->items[i+1];
form->nitems--;
}
/*
* clone a field from the form definition. This is used when the form is
* cloned when the form editor starts up with the form that is also being
* displayed (never operate on the displayed form directly).
*/
ITEM *item_clone(
register ITEM *parent) /* item to clone */
{
register ITEM *item; /* target item */
int i;
if (!(item = (ITEM *)malloc(sizeof(ITEM)))) {
create_error_popup(toplevel, errno, "Can't clone field");
return(0);
}
*item = *parent;
item->name = mystrdup(parent->name);
item->label = mystrdup(parent->label);
item->flagcode = mystrdup(parent->flagcode);
item->gray_if = mystrdup(parent->gray_if);
item->freeze_if = mystrdup(parent->freeze_if);
item->invisible_if = mystrdup(parent->invisible_if);
item->skip_if = mystrdup(parent->skip_if);
item->idefault = mystrdup(parent->idefault);
item->pattern = mystrdup(parent->pattern);
item->pressed = mystrdup(parent->pressed);
item->added = mystrdup(parent->added);
item->ch_xexpr = mystrdup(parent->ch_xexpr);
item->ch_yexpr = mystrdup(parent->ch_yexpr);
item->database = mystrdup(parent->database);
item->query = mystrdup(parent->query);
item->ch_bar = 0;
item->ch_nbars = 0;
if (item->ch_comp) {
item->ch_comp = (CHART*)malloc(item->ch_ncomp * sizeof(CHART));
for (i=0; i < item->ch_ncomp; i++)
clone_chart_component(&item->ch_comp[i],
&parent->ch_comp[i]);
}
return(item);
}